package elogger

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"time"

	"github.com/lestrrat-go/file-rotatelogs"
	"github.com/rifflock/lfshook"
	"github.com/sirupsen/logrus"
)

type ELoggerConfig struct {
	Path       string `ini:"path"`
	Level      string `ini:"level"`
	FilePrefix string `ini:"fileprefix"`
	Console    bool   `ini:"console"`
}

type ELoggerFormater struct {
	logrus.Formatter
	Pid      int
	Hostname string
}

var elogger *logrus.Logger
var hostname string
var pid int

/*
 *  默认路径 ./logs
 *  日志按照小时切分
 *  日志文件名 [Path]/[Prefix]_%Y-%m-%d-%H.log
 *  日志等级 warn < info < debug < trace
 *  未初始化默认使用golang原生log
 */
func InitELogger(conf *ELoggerConfig) {
	if elogger != nil {
		return
	}

	elogger = logrus.New()

	// log level
	logLevel, err := logrus.ParseLevel(conf.Level)
	if err != nil {
		fmt.Println("use default log level: info")
		logLevel = logrus.InfoLevel
	}
	elogger.SetLevel(logLevel)

	//format
	hostname, err = os.Hostname()
	if err != nil {
		hostname = "unknown"
	}
	formater := ELoggerFormater{Hostname: hostname, Pid: os.Getpid()}
	elogger.SetFormatter(formater)

	//logfile
	if conf.Path != "" {
		logPath := conf.Path

		// log path
		logFileName := "%Y-%m-%d-%H.log"
		if conf.FilePrefix != "" {
			logFileName = conf.FilePrefix + "_" + logFileName
		}
		logWriter, err := rotatelogs.New(
			path.Join(logPath, logFileName),
			rotatelogs.WithMaxAge(7*24*time.Hour),
			rotatelogs.WithRotationTime(1*time.Hour),
		)
		if err != nil {
			panic("GetLogger failed. Error is " + err.Error())
		}
		// set hook
		writeMap := lfshook.WriterMap{}
		for i := logrus.PanicLevel; i <= logrus.TraceLevel; i++ {
			writeMap[i] = logWriter
		}

		elogger.Hooks.Add(lfshook.NewHook(
			writeMap,
			&ELoggerFormater{Hostname: hostname, Pid: os.Getpid()},
		))
	}

	// disable console
	if !conf.Console {
		elogger.SetOutput(ioutil.Discard)
	}
}

func (f ELoggerFormater) Format(entry *logrus.Entry) ([]byte, error) {
	var filedsJson []byte
	if len(entry.Data) != 0 {
		filedsJson, _ = json.Marshal(entry.Data)
	}

	logContent := fmt.Sprintf("%s_%d [%d] [%s] [%s] [%s] %s\n",
		entry.Time.Format(time.RFC3339),
		entry.Time.UnixNano(),
		f.Pid,
		entry.Level.String(),
		f.Hostname,
		string(filedsJson),
		entry.Message)

	return append([]byte(logContent)), nil
}

func Panic(args ...interface{}) {
	if elogger == nil {
		log.Println(args...)
		return
	}
	elogger.Panic(args...)
}
func Panicf(format string, args ...interface{}) {
	if elogger == nil {
		log.Printf(format, args...)
		return
	}
	elogger.Panicf(format, args...)
}
func Error(args ...interface{}) {
	if elogger == nil {
		log.Println(args...)
		return
	}
	elogger.Error(args...)
}
func Errorf(format string, args ...interface{}) {
	if elogger == nil {
		log.Printf(format, args...)
		return
	}
	elogger.Errorf(format, args...)
}
func Warn(args ...interface{}) {
	if elogger == nil {
		log.Println(args...)
		return
	}
	elogger.Warn(args...)
}
func Warnf(format string, args ...interface{}) {
	if elogger == nil {
		log.Printf(format, args...)
		return
	}
	elogger.Warnf(format, args...)
}
func Info(args ...interface{}) {
	if elogger == nil {
		log.Println(args...)
		return
	}
	elogger.Info(args...)
}
func Infof(format string, args ...interface{}) {
	if elogger == nil {
		log.Printf(format, args...)
		return
	}
	elogger.Infof(format, args...)
}
func Debug(args ...interface{}) {
	if elogger == nil {
		log.Println(args...)
		return
	}
	elogger.Debug(args...)
}
func Debugf(format string, args ...interface{}) {
	if elogger == nil {
		log.Printf(format, args...)
		return
	}
	elogger.Debugf(format, args...)
}
func Trace(args ...interface{}) {
	if elogger == nil {
		log.Println(args...)
		return
	}
	elogger.Trace(args...)
}
func Tracef(format string, args ...interface{}) {
	if elogger == nil {
		log.Printf(format, args...)
		return
	}
	elogger.Tracef(format, args...)
}

func InfoWithFields(fields logrus.Fields, args ...interface{}) {
	if elogger == nil {
		log.Println("using elogger without init, with fields mode not work")
		return
	}
	elogger.WithFields(fields).Info(args...)
}

func ErrorWithFields(fields logrus.Fields, args ...interface{}) {
	if elogger == nil {
		log.Println("using elogger without init, with fields mode not work")
		return
	}
	elogger.WithFields(fields).Error(args...)
}

func GetELogger() *logrus.Logger {
	return elogger
}
